package thread;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import thread.learn.TestRunnale;

import java.util.concurrent.*;

/**
 * 1:多线程有几种实现方案，分别是哪几种?
 * 两种。
 * 继承 Thread 类
 * 实现 Runnable 接口
 * 扩展一种：实现 Callable 接口。这个得和线程池结合。
 * 2:同步有几种方式，分别是什么?
 * 两种。
 * 同步代码块
 * 同步方法
 * 3:启动一个线程是 run()还是 start()?它们的区别?
 * start();
 * run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用
 * start():启动线程，并由 JVM 自动调用 run()方法
 * 4:sleep()和 wait()方法的区别
 * sleep():必须指时间;不释放锁。
 * wait():可以不指定时间，也可以指定时间;释放锁。
 * 5:为什么 wait(),notify(),notifyAll()等方法都定义在 Object 类中
 * 因为这些方法的调用是依赖于锁对象的，而同步代码块的锁对象是任意锁。
 * 而 Object 代码任意的对象，所以，定义在这里面。
 * 6:线程的生命周期图
 * 新建 -- 就绪 -- 运行 -- 死亡
 * 新建 -- 就绪 -- 运行 -- 阻塞 -- 就
 *
 * 线程池的参数，阻塞队列的实现
 * @author jiangwz
 * @create 2019/8/27.
 */
@Slf4j
public class ThreadPoolExecutorTest {



    /**
     *     public ThreadPoolExecutor(int corePoolSize,
     *                               int maximumPoolSize,
     *                               long keepAliveTime,
     *                               TimeUnit unit,
     *                               BlockingQueue<Runnable> workQueue,
     *                               ThreadFactory threadFactory,
     *                               RejectedExecutionHandler handler) {
     *
     *
     *   corePoolSize :  核心线程数。在创建了线程池后，线程中没有任何线程，等到有任务到来时才创建线程去执行任务。
     *   默认情况下，在创建了线程池后，线程池中的线程数为0，当有任务来之后，
     *   就会创建一个线程去执行任务，当线程池中的线程数目达到corePoolSize后，就会把到达的任务放到缓存队列当中。
     *
     *   maximumPoolSize :
     *   当线程数大于或等于核心线程，且任务队列已满时，线程池会创建新的线程，直到线程数量达到maxPoolSize。
     *   如果线程数已等于maxPoolSize，且任务队列已满，则已超出线程池的处理能力，线程池会拒绝处理任务而抛出异常。
     *
     *   keepAliveTime :
     *   当线程空闲时间达到keepAliveTime，该线程会退出，直到线程数量等于corePoolSize。
     *   如果allowCoreThreadTimeout设置为true，则所有线程均会退出直到线程数量为0。
     *
     *   BlockingQueue<Runnable> workQueue ：
     *   阻塞队列，存储等待执行的任务。参数有ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue可选。
     *
     *   RejectedExecutionHandler :
     *   队列已满，而且任务量大于最大线程的异常处理策略。有以下取值
     *   ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
     *   ThreadPoolExecutor.DiscardPolicy：也是丢弃任务，但是不抛出异常。
     *   ThreadPoolExecutor.DiscardOldestPolicy：丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）
     *   ThreadPoolExecutor.CallerRunsPolicy：由调用线程处理该任务
     */

//    private static final RejectedExecutionHandler defaultHandler =
//            new ThreadPoolExecutor.DiscardPolicy();
        private static final RejectedExecutionHandler defaultHandler =
            new ThreadPoolExecutor.AbortPolicy();

    @Test
    public void test01(){
        int size = Runtime.getRuntime().availableProcessors();
        System.out.println("size= "+size);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2,
                size,
                60L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                defaultHandler); //
        for (int i = 0; i < 10; i++) {
            try {
                System.out.println("提交第"+i+"个线程");
                executor.submit(new TestRunnale());
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


        System.out.println("循环结束");
    }

    /**
     * 阻塞队列的实现 LinkedBlockingQueue
     * final AtomicInteger count = this.count;
     * put方法 在put的时候首先判断count 是否等于 capacity
     * 如果等于（满了）就进行休眠，等待其他线程唤醒(poll/take 消费后唤醒)
     * 如果不等于，先进队列，进了队列后，发现队列没满，唤醒没满的锁，通知可以继续往队列里面放元素
     * 然后判断是不是第一个进的队列，是的话就唤醒 poll/take的锁，通知他们已经有数据了，可以去拿了
     *
     * take方法  在take的时候 判断count是不是等于0
     * 如果等于0休眠，等待 put等方法唤醒
     * 如果不等于0 出列，然后唤醒不为空的锁，
     * 如果出列的那个元素正好是刚满的那个，则唤醒put线程，通知他可以放数据了
     *
     * 其实就是通过锁互相唤醒通知，有数据了就通知消费
     */
    @Test
    public void test02() throws InterruptedException {

        LinkedBlockingQueue<Object> queue = new LinkedBlockingQueue<>(3);
        CountDownLatch latch = new CountDownLatch(1);
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    queue.put(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }).start();


        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                    queue.poll();
            }

        }).start();

        latch.await();
    }

    @Test
    public void testAvailableProcessors() throws InterruptedException {

        /**
         * 我本机是12个处理器
         */
        System.out.println(Runtime.getRuntime().availableProcessors());//12
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        try {
            for (int i = 0; i < 12; i++) {

                int finalI = i;
                executorService.execute(()-> {

                    System.out.println(finalI);
                    System.out.println(Thread.currentThread().getName());
                    try {

                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
//            executorService.shutdown();
        }

        //要让线程等待所有线程处理完在结束,不然程序结束了,线程也就停了
        Thread.currentThread().join();

    }

    @Test
    public void testThreadOOM(){
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        while (true){
            executorService.submit(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    @Test
    public void submitNotPrintError() throws ExecutionException, InterruptedException {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat("judge-pool-%d")
                .setUncaughtExceptionHandler((thread, throwable)-> log.info("ThreadPool {} got exception", thread,throwable))
                .build();
        ExecutorService  executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(Integer.MAX_VALUE),namedThreadFactory);
        while (true){
            executor.execute(() -> {
                System.out.println(3 / 0);
            });
//            System.out.println(submit.get());

        }
    }

    @Test
    public void test007(){
        ExecutorService executorService = Executors.newSingleThreadExecutor();
    }

    public static void main(String[] args){


        ExecutorService executorService = Executors.newSingleThreadExecutor();
        while (true){
            executorService.execute(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(3 / 0);
            });

        }

    }
}
